/**
 * @file log.c
 * @author Letter (nevermindzzt@gmail.com)
 * @brief log
 * @version 1.0.0
 * @date 2020-07-30
 *
 * @copyright (c) 2020 Letter
 *
 */
#include "log.h"
#include "stdio.h"
#include "stdarg.h"

#if LOG_USING_COLOR == 1
#define memPrintHead CSI(91) \
    "    OFFSET: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" \
    CSI(97) \
    "\r\n"
#define memPrintAddr CSI(91)"0x%08X: "CSI(96)
#elif LOG_USING_COLOR == 2
#define memPrintHead RTT_CTRL_BG_MAGENTA \
    "    OFFSET: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" \
    RTT_CTRL_RESET \
    "\r\n"
#define memPrintAddr RTT_CTRL_BG_MAGENTA"0x%08X:"RTT_CTRL_RESET" "
#else
#define memPrintHead "    OFFSET: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n"
#define memPrintAddr "0x%08X: "
#endif

Log *logList[LOG_MAX_NUMBER] = {0};
static char logBuffer[LOG_BUFFER_SIZE];

#if LOG_USING_LOCK == 1
/**
 * @brief   上锁log对象
 * @param   log log对象
 */
static void logLock(Log *log)
{
    if (log == LOG_ALL_OBJ)
    {
        for (short i = 0; i < LOG_MAX_NUMBER; i++)
        {
            if (logList[i] && logList[i]->active)
            {
                if (logList[i]->lock)
                {
                    LOG_LOCK(logList[i]);
                }
            }
        }
    }
    else if (log->lock)
    {
        LOG_LOCK(log);
    }
}

/**
 * @brief   解锁log对象
 * @param   log log对象
 */
static void logUnlock(Log *log)
{
    if (log == LOG_ALL_OBJ)
    {
        for (short i = 0; i < LOG_MAX_NUMBER; i++)
        {
            if (logList[i] && logList[i]->active)
            {
                if (logList[i]->unlock)
                {
                    LOG_UNLOCK(logList[i]);
                }
            }
        }
    }
    else if (log->unlock)
    {
        LOG_UNLOCK(log);
    }
}
#endif /* LOG_USING_LOCK == 1 */

/**
 * @brief 注册log对象
 *
 * @param log log对象
 */
void logRegister(Log *log)
{
    for (short i = 0; i < LOG_MAX_NUMBER; i++)
    {
        if (logList[i] == 0)
        {
            logList[i] = log;
            return;
        }
    }

    // log->write((char *)"\033[2J\033[1H", strlen("\033[2J\033[1H"));
}


/**
 * @brief 注销log对象
 *
 * @param log log对象
 */
void logUnRegister(Log *log)
{
    for (short i = 0; i < LOG_MAX_NUMBER; i++)
    {
        if (logList[i] == log)
        {
            logList[i] = 0;
            return;
        }
    }
}


/**
 * @brief 设置log日志级别
 *
 * @param log log对象
 * @param level 日志级别
 */
void logSetLevel(Log *log, LogLevel level)
{
    logAssert(log, return);
    log->level = level;
}



/**
 * @brief 自定义logSetLevel
 *
 */
void logLevelSet(LogLevel level)
{
    logSetLevel(LOG_ALL_OBJ, level);
}

/**
 * @brief log写buffer
 *
 * @param log log对象
 * @param level 日志级别
 * @param buffer buffer
 * @param len buffer长度
 */
static void logWriteBuffer(Log *log, LogLevel level, char *buffer, short len)
{
#if LOG_USING_LOCK == 1
    logLock(log);
#endif /* LOG_USING_LOCK == 1 */
    if (log == LOG_ALL_OBJ)
    {
        for (short i = 0; i < LOG_MAX_NUMBER; i++)
        {
            if (logList[i]
                && logList[i]->active
                && logList[i]->level >= level)
            {
                logList[i]->write(logBuffer, len);
            }
        }
    }
    else if (log && log->active && log->level >= level)
    {
        log->write(logBuffer, len);
    }
#if LOG_USING_LOCK == 1
    logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}

/**
 * @brief log格式化写入数据
 *
 * @param log log对象
 * @param level log级别
 * @param fmt 格式
 * @param ... 参数
 */
void logWrite(Log *log, LogLevel level, const char *fmt, ...)	// xNOTE char *fmt -> const char *fmt
{
    va_list vargs;
    short len;

#if LOG_USING_LOCK == 1
    logLock(log);
#endif /* LOG_USING_LOCK == 1 */
    va_start(vargs, fmt);
    len = vsnprintf(logBuffer, LOG_BUFFER_SIZE - 1, fmt, vargs);
    va_end(vargs);

    logWriteBuffer(log, level, logBuffer, len);
#if LOG_USING_LOCK == 1
    logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}

/**
 * @brief 16进制输出
 *
 * @param log log对象
 * @param level 以哪个日志级别打印
 * @param base 内存基址
 * @param length 长度
 */
void logHexDumpSingle(Log *log, LogLevel level, void *base, unsigned int length)
{
    unsigned char *address;
    unsigned int len;
    unsigned int printLen = 0;

    if (length == 0 || (log != LOG_ALL_OBJ && log->level < level))
    {
        return;
    }
#if LOG_USING_LOCK == 1
    logLock(log);
#endif /* LOG_USING_LOCK == 1 */
    len = snprintf(logBuffer, LOG_BUFFER_SIZE - 1, "Memory of 0x%08X, Size: [%d]:\r\n%s",
                   (unsigned int)base, length, memPrintHead);
    logWriteBuffer(log, level, logBuffer, len);

    len = length;

    address = (unsigned char *)((unsigned int)base & (~0x0000000F));
    length += (unsigned int)base - (unsigned int)address;
    length = (length + 15) & (~0x0000000F);

    while (length)
    {
        printLen += sprintf(logBuffer + printLen, memPrintAddr, (unsigned int)address);
        for (int i = 0; i < 16; i++)
        {
            if ((unsigned int)(address + i) < (unsigned int)base
                || (unsigned int)(address + i) >= (unsigned int)base + len)
            {
                logBuffer[printLen ++] = ' ';
                logBuffer[printLen ++] = ' ';
                logBuffer[printLen ++] = ' ';
            }
            else
            {
                printLen += sprintf(logBuffer + printLen, "%02X ", *(address + i));
            }
        }
        logBuffer[printLen ++] = '|';
        logBuffer[printLen ++] = ' ';
        for (int i = 0; i < 16; i++)
        {
            if ((unsigned int)(address + i) < (unsigned int)base
                || (unsigned int)(address + i) >= (unsigned int)base + len)
            {
                logBuffer[printLen ++] = ' ';
            }
            else
            {
                if (*(address + i) >= 32 && *(address + i) <= 126)
                {
                    printLen += sprintf(logBuffer + printLen, "%c", *(address + i));
                }
                else
                {
                    logBuffer[printLen ++] = '.';
                }
            }
        }
        logBuffer[printLen ++] = ' ';
        logBuffer[printLen ++] = '|';
        logBuffer[printLen ++] = '\r';
        logBuffer[printLen ++] = '\n';
        logWriteBuffer(log, level, logBuffer, printLen);
        address += 16;
        length -= 16;
        printLen = 0;
    }
#if LOG_USING_LOCK == 1
    logUnlock(log);
#endif /* LOG_USING_LOCK == 1 */
}


